home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / pyinotify.py < prev    next >
Text File  |  2009-08-04  |  64KB  |  1,817 lines

  1. #!/usr/bin/env python
  2. # -*- coding: iso-8859-1 -*-
  3. #
  4. # pyinotify.py - python interface to inotify
  5. # Copyright (C) SΘbastien Martini <sebastien.martini@gmail.com>
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU General Public License
  9. # as published by the Free Software Foundation; either version 2
  10. # of the License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20. # 02110-1301, USA.
  21.  
  22. """
  23. pyinotify
  24.  
  25. @author: Sebastien Martini
  26. @license: GPLv2+
  27. @contact: seb@dbzteam.org
  28. """
  29.  
  30. class PyinotifyError(Exception):
  31.     """Indicates exceptions raised by a Pyinotify class."""
  32.  
  33.  
  34. class UnsupportedPythonVersionError(PyinotifyError):
  35.     """
  36.     Raised for unsupported Python version.
  37.     """
  38.     def __init__(self, version):
  39.         """
  40.         @param version: Current Python version
  41.         @type version: string
  42.         """
  43.         PyinotifyError.__init__(self,
  44.                                 ('Python %s is unsupported, requires '
  45.                                 'at least Python 2.4') % version)
  46.  
  47.  
  48. class UnsupportedLibcVersionError(PyinotifyError):
  49.     """
  50.     Raised for unsupported libc version.
  51.     """
  52.     def __init__(self, version):
  53.         """
  54.         @param version: Current Libc version
  55.         @type version: string
  56.         """
  57.         PyinotifyError.__init__(self,
  58.                                 ('Libc %s is unsupported, requires '
  59.                                 'at least Libc 2.4') % version)
  60.  
  61.  
  62. # Check Python version
  63. import sys
  64. if sys.version < '2.4':
  65.     raise UnsupportedPythonVersionError(sys.version)
  66.  
  67.  
  68. # Import directives
  69. import threading
  70. import os
  71. import select
  72. import struct
  73. import fcntl
  74. import errno
  75. import termios
  76. import array
  77. import logging
  78. import atexit
  79. from collections import deque
  80. from datetime import datetime, timedelta
  81. import time
  82. import fnmatch
  83. import re
  84. import ctypes
  85. import ctypes.util
  86.  
  87.  
  88. __author__ = "seb@dbzteam.org (Sebastien Martini)"
  89.  
  90. __version__ = "0.8.6"
  91.  
  92. __metaclass__ = type  # Use new-style classes by default
  93.  
  94.  
  95. # load libc
  96. LIBC = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
  97.  
  98. # the libc version check.
  99. # XXX: Maybe it is better to check if the libc has the needed functions inside?
  100. #      Because there are inotify patches for libc 2.3.6.
  101. LIBC.gnu_get_libc_version.restype = ctypes.c_char_p
  102. LIBC_VERSION = LIBC.gnu_get_libc_version()
  103. if (int(LIBC_VERSION.split('.')[0]) < 2 or
  104.     (int(LIBC_VERSION.split('.')[0]) == 2 and
  105.      int(LIBC_VERSION.split('.')[1]) < 4)):
  106.     raise UnsupportedLibcVersionError(LIBC_VERSION)
  107.  
  108.  
  109. # logging
  110. log = logging.getLogger("pyinotify")
  111. console_handler = logging.StreamHandler()
  112. console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
  113. log.addHandler(console_handler)
  114. log.setLevel(20)
  115.  
  116.  
  117. # Try to speed-up execution with psyco
  118. try:
  119.     if False:
  120.         import psyco
  121.         psyco.full()
  122. except ImportError:
  123.     # Cannot import psyco
  124.     pass
  125.  
  126.  
  127. ### inotify's variables ###
  128.  
  129.  
  130. class SysCtlINotify:
  131.     """
  132.     Access (read, write) inotify's variables through sysctl.
  133.  
  134.     Examples:
  135.       - Read variable: myvar = max_queued_events.value
  136.       - Update variable: max_queued_events.value = 42
  137.     """
  138.  
  139.     inotify_attrs = {'max_user_instances': 1,
  140.                      'max_user_watches': 2,
  141.                      'max_queued_events': 3}
  142.  
  143.     def __init__(self, attrname):
  144.         sino = ctypes.c_int * 3
  145.         self._attrname = attrname
  146.         self._attr = sino(5, 20, SysCtlINotify.inotify_attrs[attrname])
  147.  
  148.     def get_val(self):
  149.         """
  150.         @return: stored value.
  151.         @rtype: int
  152.         """
  153.         oldv = ctypes.c_int(0)
  154.         size = ctypes.c_int(ctypes.sizeof(oldv))
  155.         LIBC.sysctl(self._attr, 3,
  156.                     ctypes.c_voidp(ctypes.addressof(oldv)),
  157.                     ctypes.addressof(size),
  158.                     None, 0)
  159.         return oldv.value
  160.  
  161.     def set_val(self, nval):
  162.         """
  163.         @param nval: set to nval.
  164.         @type nval: int
  165.         """
  166.         oldv = ctypes.c_int(0)
  167.         sizeo = ctypes.c_int(ctypes.sizeof(oldv))
  168.         newv = ctypes.c_int(nval)
  169.         sizen = ctypes.c_int(ctypes.sizeof(newv))
  170.         LIBC.sysctl(self._attr, 3,
  171.                     ctypes.c_voidp(ctypes.addressof(oldv)),
  172.                     ctypes.addressof(sizeo),
  173.                     ctypes.c_voidp(ctypes.addressof(newv)),
  174.                     ctypes.addressof(sizen))
  175.  
  176.     value = property(get_val, set_val)
  177.  
  178.     def __repr__(self):
  179.         return '<%s=%d>' % (self._attrname, self.get_val())
  180.  
  181.  
  182. # singleton instances
  183. #
  184. # read int: myvar = max_queued_events.value
  185. # update: max_queued_events.value = 42
  186. #
  187. for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'):
  188.     globals()[attrname] = SysCtlINotify(attrname)
  189.  
  190.  
  191. # fixme: put those tests elsewhere
  192. #
  193. # print max_queued_events
  194. # print max_queued_events.value
  195. # save = max_queued_events.value
  196. # print save
  197. # max_queued_events.value += 42
  198. # print max_queued_events
  199. # max_queued_events.value = save
  200. # print max_queued_events
  201.  
  202.  
  203. ### iglob ###
  204.  
  205.  
  206. # Code taken from standart Python Lib, slightly modified in order to work
  207. # with pyinotify (don't exclude dotted files/dirs like .foo).
  208. # Original version:
  209. # @see: http://svn.python.org/projects/python/trunk/Lib/glob.py
  210.  
  211. def iglob(pathname):
  212.     if not has_magic(pathname):
  213.         if hasattr(os.path, 'lexists'):
  214.             if os.path.lexists(pathname):
  215.                 yield pathname
  216.         else:
  217.             if os.path.islink(pathname) or os.path.exists(pathname):
  218.                 yield pathname
  219.         return
  220.     dirname, basename = os.path.split(pathname)
  221.     # relative pathname
  222.     if not dirname:
  223.         return
  224.     # absolute pathname
  225.     if has_magic(dirname):
  226.         dirs = iglob(dirname)
  227.     else:
  228.         dirs = [dirname]
  229.     if has_magic(basename):
  230.         glob_in_dir = glob1
  231.     else:
  232.         glob_in_dir = glob0
  233.     for dirname in dirs:
  234.         for name in glob_in_dir(dirname, basename):
  235.             yield os.path.join(dirname, name)
  236.  
  237. def glob1(dirname, pattern):
  238.     if not dirname:
  239.         dirname = os.curdir
  240.     try:
  241.         names = os.listdir(dirname)
  242.     except os.error:
  243.         return []
  244.     return fnmatch.filter(names, pattern)
  245.  
  246. def glob0(dirname, basename):
  247.     if basename == '' and os.path.isdir(dirname):
  248.         # `os.path.split()` returns an empty basename for paths ending with a
  249.         # directory separator.  'q*x/' should match only directories.
  250.         return [basename]
  251.     if hasattr(os.path, 'lexists'):
  252.         if os.path.lexists(os.path.join(dirname, basename)):
  253.             return [basename]
  254.     else:
  255.         if (os.path.islink(os.path.join(dirname, basename)) or
  256.             os.path.exists(os.path.join(dirname, basename))):
  257.             return [basename]
  258.     return []
  259.  
  260. magic_check = re.compile('[*?[]')
  261.  
  262. def has_magic(s):
  263.     return magic_check.search(s) is not None
  264.  
  265.  
  266.  
  267. ### Core ###
  268.  
  269.  
  270. class EventsCodes:
  271.     """
  272.     Set of codes corresponding to each kind of events.
  273.     Some of these flags are used to communicate with inotify, whereas
  274.     the others are sent to userspace by inotify notifying some events.
  275.  
  276.     @cvar IN_ACCESS: File was accessed.
  277.     @type IN_ACCESS: int
  278.     @cvar IN_MODIFY: File was modified.
  279.     @type IN_MODIFY: int
  280.     @cvar IN_ATTRIB: Metadata changed.
  281.     @type IN_ATTRIB: int
  282.     @cvar IN_CLOSE_WRITE: Writtable file was closed.
  283.     @type IN_CLOSE_WRITE: int
  284.     @cvar IN_CLOSE_NOWRITE: Unwrittable file closed.
  285.     @type IN_CLOSE_NOWRITE: int
  286.     @cvar IN_OPEN: File was opened.
  287.     @type IN_OPEN: int
  288.     @cvar IN_MOVED_FROM: File was moved from X.
  289.     @type IN_MOVED_FROM: int
  290.     @cvar IN_MOVED_TO: File was moved to Y.
  291.     @type IN_MOVED_TO: int
  292.     @cvar IN_CREATE: Subfile was created.
  293.     @type IN_CREATE: int
  294.     @cvar IN_DELETE: Subfile was deleted.
  295.     @type IN_DELETE: int
  296.     @cvar IN_DELETE_SELF: Self (watched item itself) was deleted.
  297.     @type IN_DELETE_SELF: int
  298.     @cvar IN_MOVE_SELF: Self (watched item itself) was moved.
  299.     @type IN_MOVE_SELF: int
  300.     @cvar IN_UNMOUNT: Backing fs was unmounted.
  301.     @type IN_UNMOUNT: int
  302.     @cvar IN_Q_OVERFLOW: Event queued overflowed.
  303.     @type IN_Q_OVERFLOW: int
  304.     @cvar IN_IGNORED: File was ignored.
  305.     @type IN_IGNORED: int
  306.     @cvar IN_ONLYDIR: only watch the path if it is a directory (new
  307.                       in kernel 2.6.15).
  308.     @type IN_ONLYDIR: int
  309.     @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15).
  310.                           IN_ONLYDIR we can make sure that we don't watch
  311.                           the target of symlinks.
  312.     @type IN_DONT_FOLLOW: int
  313.     @cvar IN_MASK_ADD: add to the mask of an already existing watch (new
  314.                        in kernel 2.6.14).
  315.     @type IN_MASK_ADD: int
  316.     @cvar IN_ISDIR: Event occurred against dir.
  317.     @type IN_ISDIR: int
  318.     @cvar IN_ONESHOT: Only send event once.
  319.     @type IN_ONESHOT: int
  320.     @cvar ALL_EVENTS: Alias for considering all of the events.
  321.     @type ALL_EVENTS: int
  322.     """
  323.  
  324.     # The idea here is 'configuration-as-code' - this way, we get our nice class
  325.     # constants, but we also get nice human-friendly text mappings to do lookups
  326.     # against as well, for free:
  327.     FLAG_COLLECTIONS = {'OP_FLAGS': {
  328.         'IN_ACCESS'        : 0x00000001,  # File was accessed
  329.         'IN_MODIFY'        : 0x00000002,  # File was modified
  330.         'IN_ATTRIB'        : 0x00000004,  # Metadata changed
  331.         'IN_CLOSE_WRITE'   : 0x00000008,  # Writable file was closed
  332.         'IN_CLOSE_NOWRITE' : 0x00000010,  # Unwritable file closed
  333.         'IN_OPEN'          : 0x00000020,  # File was opened
  334.         'IN_MOVED_FROM'    : 0x00000040,  # File was moved from X
  335.         'IN_MOVED_TO'      : 0x00000080,  # File was moved to Y
  336.         'IN_CREATE'        : 0x00000100,  # Subfile was created
  337.         'IN_DELETE'        : 0x00000200,  # Subfile was deleted
  338.         'IN_DELETE_SELF'   : 0x00000400,  # Self (watched item itself)
  339.                                           # was deleted
  340.         'IN_MOVE_SELF'     : 0x00000800,  # Self (watched item itself) was moved
  341.         },
  342.                         'EVENT_FLAGS': {
  343.         'IN_UNMOUNT'       : 0x00002000,  # Backing fs was unmounted
  344.         'IN_Q_OVERFLOW'    : 0x00004000,  # Event queued overflowed
  345.         'IN_IGNORED'       : 0x00008000,  # File was ignored
  346.         },
  347.                         'SPECIAL_FLAGS': {
  348.         'IN_ONLYDIR'       : 0x01000000,  # only watch the path if it is a
  349.                                           # directory
  350.         'IN_DONT_FOLLOW'   : 0x02000000,  # don't follow a symlink
  351.         'IN_MASK_ADD'      : 0x20000000,  # add to the mask of an already
  352.                                           # existing watch
  353.         'IN_ISDIR'         : 0x40000000,  # event occurred against dir
  354.         'IN_ONESHOT'       : 0x80000000,  # only send event once
  355.         },
  356.                         }
  357.  
  358.     def maskname(mask):
  359.         """
  360.         Return the event name associated to mask. IN_ISDIR is appended when
  361.         appropriate. Note: only one event is returned, because only one is
  362.         raised once at a time.
  363.  
  364.         @param mask: mask.
  365.         @type mask: int
  366.         @return: event name.
  367.         @rtype: str
  368.         """
  369.         ms = mask
  370.         name = '%s'
  371.         if mask & IN_ISDIR:
  372.             ms = mask - IN_ISDIR
  373.             name = '%s|IN_ISDIR'
  374.         return name % EventsCodes.ALL_VALUES[ms]
  375.  
  376.     maskname = staticmethod(maskname)
  377.  
  378.  
  379. # So let's now turn the configuration into code
  380. EventsCodes.ALL_FLAGS = {}
  381. EventsCodes.ALL_VALUES = {}
  382. for flagc, valc in EventsCodes.FLAG_COLLECTIONS.iteritems():
  383.     # Make the collections' members directly accessible through the
  384.     # class dictionary
  385.     setattr(EventsCodes, flagc, valc)
  386.  
  387.     # Collect all the flags under a common umbrella
  388.     EventsCodes.ALL_FLAGS.update(valc)
  389.  
  390.     # Make the individual masks accessible as 'constants' at globals() scope
  391.     # and masknames accessible by values.
  392.     for name, val in valc.iteritems():
  393.         globals()[name] = val
  394.         EventsCodes.ALL_VALUES[val] = name
  395.  
  396.  
  397. # all 'normal' events
  398. ALL_EVENTS = reduce(lambda x, y: x | y, EventsCodes.OP_FLAGS.itervalues())
  399. EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS
  400. EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS'
  401.  
  402.  
  403. class _Event:
  404.     """
  405.     Event structure, represent events raised by the system. This
  406.     is the base class and should be subclassed.
  407.  
  408.     """
  409.     def __init__(self, dict_):
  410.         """
  411.         Attach attributes (contained in dict_) to self.
  412.         """
  413.         for tpl in dict_.iteritems():
  414.             setattr(self, *tpl)
  415.  
  416.     def __repr__(self):
  417.         """
  418.         @return: String representation.
  419.         @rtype: str
  420.         """
  421.         s = ''
  422.         for attr, value in sorted(self.__dict__.items(), key=lambda x: x[0]):
  423.             if attr.startswith('_'):
  424.                 continue
  425.             if attr == 'mask':
  426.                 value = hex(getattr(self, attr))
  427.             elif isinstance(value, str) and not value:
  428.                 value ="''"
  429.             s += ' %s%s%s' % (Color.FieldName(attr),
  430.                               Color.Punctuation('='),
  431.                               Color.FieldValue(value))
  432.  
  433.         s = '%s%s%s %s' % (Color.Punctuation('<'),
  434.                            Color.ClassName(self.__class__.__name__),
  435.                            s,
  436.                            Color.Punctuation('>'))
  437.         return s
  438.  
  439.  
  440. class _RawEvent(_Event):
  441.     """
  442.     Raw event, it contains only the informations provided by the system.
  443.     It doesn't infer anything.
  444.     """
  445.     def __init__(self, wd, mask, cookie, name):
  446.         """
  447.         @param wd: Watch Descriptor.
  448.         @type wd: int
  449.         @param mask: Bitmask of events.
  450.         @type mask: int
  451.         @param cookie: Cookie.
  452.         @type cookie: int
  453.         @param name: Basename of the file or directory against which the
  454.                      event was raised, in case where the watched directory
  455.                      is the parent directory. None if the event was raised
  456.                      on the watched item itself.
  457.         @type name: string or None
  458.         """
  459.         # name: remove trailing '\0'
  460.         super(_RawEvent, self).__init__({'wd': wd,
  461.                                          'mask': mask,
  462.                                          'cookie': cookie,
  463.                                          'name': name.rstrip('\0')})
  464.         log.debug(repr(self))
  465.  
  466.  
  467. class Event(_Event):
  468.     """
  469.     This class contains all the useful informations about the observed
  470.     event. However, the incorporation of each field is not guaranteed and
  471.     depends on the type of event. In effect, some fields are irrelevant
  472.     for some kind of event (for example 'cookie' is meaningless for
  473.     IN_CREATE whereas it is useful for IN_MOVE_TO).
  474.  
  475.     The possible fields are:
  476.       - wd (int): Watch Descriptor.
  477.       - mask (int): Mask.
  478.       - maskname (str): Readable event name.
  479.       - path (str): path of the file or directory being watched.
  480.       - name (str): Basename of the file or directory against which the
  481.               event was raised, in case where the watched directory
  482.               is the parent directory. None if the event was raised
  483.               on the watched item itself. This field is always provided
  484.               even if the string is ''.
  485.       - pathname (str): absolute path of: path + name
  486.       - cookie (int): Cookie.
  487.       - dir (bool): is the event raised against directory.
  488.  
  489.     """
  490.     def __init__(self, raw):
  491.         """
  492.         Concretely, this is the raw event plus inferred infos.
  493.         """
  494.         _Event.__init__(self, raw)
  495.         self.maskname = EventsCodes.maskname(self.mask)
  496.         try:
  497.             if self.name:
  498.                 self.pathname = os.path.abspath(os.path.join(self.path,
  499.                                                              self.name))
  500.             else:
  501.                 self.pathname = os.path.abspath(self.path)
  502.         except AttributeError:
  503.             pass
  504.  
  505.  
  506. class ProcessEventError(PyinotifyError):
  507.     """
  508.     ProcessEventError Exception. Raised on ProcessEvent error.
  509.     """
  510.     def __init__(self, err):
  511.         """
  512.         @param err: Exception error description.
  513.         @type err: string
  514.         """
  515.         PyinotifyError.__init__(self, err)
  516.  
  517.  
  518. class _ProcessEvent:
  519.     """
  520.     Abstract processing event class.
  521.     """
  522.     def __call__(self, event):
  523.         """
  524.         To behave like a functor the object must be callable.
  525.         This method is a dispatch method. Lookup order:
  526.           1. process_MASKNAME method
  527.           2. process_FAMILY_NAME method
  528.           3. otherwise call process_default
  529.  
  530.         @param event: Event to be processed.
  531.         @type event: Event object
  532.         @return: By convention when used from the ProcessEvent class:
  533.                  - Returning False or None (default value) means keep on
  534.                  executing next chained functors (see chain.py example).
  535.                  - Returning True instead means do not execute next
  536.                    processing functions.
  537.         @rtype: bool
  538.         @raise ProcessEventError: Event object undispatchable,
  539.                                   unknown event.
  540.         """
  541.         stripped_mask = event.mask - (event.mask & IN_ISDIR)
  542.         maskname = EventsCodes.ALL_VALUES.get(stripped_mask)
  543.         if maskname is None:
  544.             raise ProcessEventError("Unknown mask 0x%08x" % stripped_mask)
  545.  
  546.         # 1- look for process_MASKNAME
  547.         meth = getattr(self, 'process_' + maskname, None)
  548.         if meth is not None:
  549.             return meth(event)
  550.         # 2- look for process_FAMILY_NAME
  551.         meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None)
  552.         if meth is not None:
  553.             return meth(event)
  554.         # 3- default call method process_default
  555.         return self.process_default(event)
  556.  
  557.     def __repr__(self):
  558.         return '<%s>' % self.__class__.__name__
  559.  
  560.  
  561. class _SysProcessEvent(_ProcessEvent):
  562.     """
  563.     There is three kind of processing according to each event:
  564.  
  565.       1. special handling (deletion from internal container, bug, ...).
  566.       2. default treatment: which is applied to most of events.
  567.       4. IN_ISDIR is never sent alone, he is piggybacked with a standart
  568.          event, he is not processed as the others events, instead, its
  569.          value is captured and appropriately aggregated to dst event.
  570.     """
  571.     def __init__(self, wm, notifier):
  572.         """
  573.  
  574.         @param wm: Watch Manager.
  575.         @type wm: WatchManager instance
  576.         @param notifier: notifier.
  577.         @type notifier: Instance of Notifier.
  578.         """
  579.         self._watch_manager = wm  # watch manager
  580.         self._notifier = notifier  # notifier
  581.         self._mv_cookie = {}  # {cookie(int): (src_path(str), date), ...}
  582.         self._mv = {}  # {src_path(str): (dst_path(str), date), ...}
  583.  
  584.     def cleanup(self):
  585.         """
  586.         Cleanup (delete) old (>1mn) records contained in self._mv_cookie
  587.         and self._mv.
  588.         """
  589.         date_cur_ = datetime.now()
  590.         for seq in [self._mv_cookie, self._mv]:
  591.             for k in seq.keys():
  592.                if (date_cur_ - seq[k][1]) > timedelta(minutes=1):
  593.                    log.debug('cleanup: deleting entry %s' % seq[k][0])
  594.                    del seq[k]
  595.  
  596.     def process_IN_CREATE(self, raw_event):
  597.         """
  598.         If the event concerns a directory and the auto_add flag of the
  599.         targetted watch is set to True, a new watch is added on this
  600.         new directory, with the same attributes's values than those of
  601.         this watch.
  602.         """
  603.         if raw_event.mask & IN_ISDIR:
  604.             watch_ = self._watch_manager._wmd.get(raw_event.wd)
  605.             if watch_.auto_add:
  606.                 addw = self._watch_manager.add_watch
  607.                 newwd = addw(os.path.join(watch_.path, raw_event.name),
  608.                              watch_.mask, proc_fun=watch_.proc_fun,
  609.                              rec=False, auto_add=watch_.auto_add)
  610.  
  611.                 # Trick to handle mkdir -p /t1/t2/t3 where t1 is watched and
  612.                 # t2 and t3 are created.
  613.                 # Since the directory is new, then everything inside it
  614.                 # must also be new.
  615.                 base = os.path.join(watch_.path, raw_event.name)
  616.                 if newwd[base] > 0:
  617.                     for name in os.listdir(base):
  618.                         inner = os.path.join(base, name)
  619.                         if (os.path.isdir(inner) and
  620.                             self._watch_manager.get_wd(inner) is None):
  621.                             # Generate (simulate) creation event for sub
  622.                             # directories.
  623.                             rawevent = _RawEvent(newwd[base],
  624.                                                  IN_CREATE | IN_ISDIR,
  625.                                                  0, name)
  626.                             self._notifier._eventq.append(rawevent)
  627.         return self.process_default(raw_event)
  628.  
  629.     def process_IN_MOVED_FROM(self, raw_event):
  630.         """
  631.         Map the cookie with the source path (+ date for cleaning).
  632.         """
  633.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  634.         path_ = watch_.path
  635.         src_path = os.path.normpath(os.path.join(path_, raw_event.name))
  636.         self._mv_cookie[raw_event.cookie] = (src_path, datetime.now())
  637.         return self.process_default(raw_event, {'cookie': raw_event.cookie})
  638.  
  639.     def process_IN_MOVED_TO(self, raw_event):
  640.         """
  641.         Map the source path with the destination path (+ date for
  642.         cleaning).
  643.         """
  644.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  645.         path_ = watch_.path
  646.         dst_path = os.path.normpath(os.path.join(path_, raw_event.name))
  647.         mv_ = self._mv_cookie.get(raw_event.cookie)
  648.         if mv_:
  649.             self._mv[mv_[0]] = (dst_path, datetime.now())
  650.         return self.process_default(raw_event, {'cookie': raw_event.cookie})
  651.  
  652.     def process_IN_MOVE_SELF(self, raw_event):
  653.         """
  654.         STATUS: the following bug has been fixed in the recent kernels (fixme:
  655.         which version ?). Now it raises IN_DELETE_SELF instead.
  656.  
  657.         Old kernels are bugged, this event is raised when the watched item
  658.         was moved, so we must update its path, but under some circumstances it
  659.         can be impossible: if its parent directory and its destination
  660.         directory aren't watched. The kernel (see include/linux/fsnotify.h)
  661.         doesn't bring us enough informations like the destination path of
  662.         moved items.
  663.         """
  664.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  665.         src_path = watch_.path
  666.         mv_ = self._mv.get(src_path)
  667.         if mv_:
  668.             watch_.path = mv_[0]
  669.         else:
  670.             log.error("The path %s of this watch %s must not "
  671.                       "be trusted anymore" % (watch_.path, watch_))
  672.             if not watch_.path.endswith('-wrong-path'):
  673.                 watch_.path += '-wrong-path'
  674.         # FIXME: should we pass the cookie even if this is not standart?
  675.         return self.process_default(raw_event)
  676.  
  677.     def process_IN_Q_OVERFLOW(self, raw_event):
  678.         """
  679.         Only signal overflow, most of the common flags are irrelevant
  680.         for this event (path, wd, name).
  681.         """
  682.         return Event({'mask': raw_event.mask})
  683.  
  684.     def process_IN_IGNORED(self, raw_event):
  685.         """
  686.         The watch descriptor raised by this event is now ignored (forever),
  687.         it can be safely deleted from watch manager dictionary.
  688.         After this event we can be sure that neither the event queue
  689.         neither the system will raise an event associated to this wd.
  690.         """
  691.         event_ = self.process_default(raw_event)
  692.         try:
  693.             del self._watch_manager._wmd[raw_event.wd]
  694.         except KeyError, err:
  695.             log.error(err)
  696.         return event_
  697.  
  698.     def process_default(self, raw_event, to_append={}):
  699.         """
  700.         Common handling for the following events:
  701.  
  702.         IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE,
  703.         IN_OPEN, IN_DELETE, IN_DELETE_SELF, IN_UNMOUNT.
  704.         """
  705.         ret = None
  706.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  707.         if raw_event.mask & (IN_DELETE_SELF | IN_MOVE_SELF):
  708.             # unfornately information not provided by the kernel
  709.             dir_ = watch_.dir
  710.         else:
  711.             dir_ = bool(raw_event.mask & IN_ISDIR)
  712.     dict_ = {'wd': raw_event.wd,
  713.                  'mask': raw_event.mask,
  714.                  'path': watch_.path,
  715.                  'name': raw_event.name,
  716.                  'dir': dir_}
  717.         dict_.update(to_append)
  718.         return Event(dict_)
  719.  
  720.  
  721. class ProcessEvent(_ProcessEvent):
  722.     """
  723.     Process events objects, can be specialized via subclassing, thus its
  724.     behavior can be overriden:
  725.  
  726.     Note: you should not override __init__ in your subclass instead define
  727.     a my_init() method, this method will be called from the constructor of
  728.     this class with optional parameters.
  729.  
  730.       1. Provide methods, e.g. process_IN_DELETE for processing a given kind
  731.          of event (eg. IN_DELETE in this case).
  732.       2. Or/and provide methods for processing events by 'family', e.g.
  733.          process_IN_CLOSE method will process both IN_CLOSE_WRITE and
  734.          IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and
  735.          process_IN_CLOSE_NOWRITE aren't defined).
  736.       3. Or/and override process_default for processing the remaining kind of
  737.          events.
  738.     """
  739.     pevent = None
  740.  
  741.     def __init__(self, pevent=None, **kargs):
  742.         """
  743.         Enable chaining of ProcessEvent instances.
  744.  
  745.         @param pevent: optional callable object, will be called on event
  746.                        processing (before self).
  747.         @type pevent: callable
  748.         @param kargs: optional arguments delagated to template method my_init
  749.         @type kargs: dict
  750.         """
  751.         self.pevent = pevent
  752.         self.my_init(**kargs)
  753.  
  754.     def my_init(self, **kargs):
  755.         """
  756.         Override this method when subclassing if you want to achieve
  757.         custom initialization of your subclass' instance. You MUST pass
  758.         keyword arguments. This method does nothing by default.
  759.  
  760.         @param kargs: optional arguments delagated to template method my_init
  761.         @type kargs: dict
  762.         """
  763.         pass
  764.  
  765.     def __call__(self, event):
  766.         stop_chaining = False
  767.         if self.pevent is not None:
  768.             # By default methods return None so we fix as guideline
  769.             # that methods asking for stop chaining must explicitely
  770.             # return non None or False values, otherwise the default
  771.             # behavior is to chain call to the corresponding local
  772.             # method.
  773.             stop_chaining = self.pevent(event)
  774.         if not stop_chaining:
  775.             return _ProcessEvent.__call__(self, event)
  776.  
  777.     def nested_pevent(self):
  778.         return self.pevent
  779.  
  780.     def process_default(self, event):
  781.         """
  782.         Default default processing event method. Print event
  783.         on standart output.
  784.  
  785.         @param event: Event to be processed.
  786.         @type event: Event instance
  787.         """
  788.         print(repr(event))
  789.  
  790.  
  791. class ChainIfTrue(ProcessEvent):
  792.     """
  793.     Makes conditional chaining depending on the result of the nested
  794.     processing instance.
  795.     """
  796.     def my_init(self, func):
  797.         self._func = func
  798.  
  799.     def process_default(self, event):
  800.         return not self._func(event)
  801.  
  802.  
  803. class Stats(ProcessEvent):
  804.     def my_init(self):
  805.         self._start_time = time.time()
  806.         self._stats = {}
  807.         self._stats_lock = threading.Lock()
  808.  
  809.     def process_default(self, event):
  810.         self._stats_lock.acquire()
  811.         try:
  812.             events = event.maskname.split('|')
  813.             for event_name in events:
  814.                 count = self._stats.get(event_name, 0)
  815.                 self._stats[event_name] = count + 1
  816.         finally:
  817.             self._stats_lock.release()
  818.  
  819.     def _stats_copy(self):
  820.         self._stats_lock.acquire()
  821.         try:
  822.             return self._stats.copy()
  823.         finally:
  824.             self._stats_lock.release()
  825.  
  826.     def __repr__(self):
  827.         stats = self._stats_copy()
  828.  
  829.         t = int(time.time() - self._start_time)
  830.         if t < 60:
  831.             ts = str(t) + 'sec'
  832.         elif 60 <= t < 3600:
  833.             ts = '%dmn%dsec' % (t / 60, t % 60)
  834.         elif 3600 <= t < 86400:
  835.             ts = '%dh%dmn' % (t / 3600, (t % 3600) / 60)
  836.         elif t >= 86400:
  837.             ts = '%dd%dh' % (t / 86400, (t % 86400) / 3600)
  838.         stats['ElapsedTime'] = ts
  839.  
  840.         l = []
  841.         for ev, value in sorted(stats.items(), key=lambda x: x[0]):
  842.             l.append(' %s=%s' % (Color.FieldName(ev),
  843.                                  Color.FieldValue(value)))
  844.         s = '<%s%s >' % (Color.ClassName(self.__class__.__name__),
  845.                          ''.join(l))
  846.         return s
  847.  
  848.     def dump(self, filename):
  849.         fo = file(filename, 'wb')
  850.         try:
  851.             fo.write(str(self))
  852.         finally:
  853.             fo.close()
  854.  
  855.     def __str__(self, scale=45):
  856.         stats = self._stats_copy()
  857.         if not stats:
  858.             return ''
  859.  
  860.         m = max(stats.values())
  861.         unity = int(round(float(m) / scale)) or 1
  862.         fmt = '%%-26s%%-%ds%%s' % (len(Color.FieldValue('@' * scale))
  863.                                    + 1)
  864.         def func(x):
  865.             return fmt % (Color.FieldName(x[0]),
  866.                           Color.FieldValue('@' * (x[1] / unity)),
  867.                           Color.Simple('%d' % x[1], 'yellow'))
  868.         s = '\n'.join(map(func, sorted(stats.items(), key=lambda x: x[0])))
  869.         return s
  870.  
  871.  
  872. class NotifierError(PyinotifyError):
  873.     """
  874.     Notifier Exception. Raised on Notifier error.
  875.  
  876.     """
  877.     def __init__(self, err):
  878.         """
  879.         @param err: Exception string's description.
  880.         @type err: string
  881.         """
  882.         PyinotifyError.__init__(self, err)
  883.  
  884.  
  885. class Notifier:
  886.     """
  887.     Read notifications, process events.
  888.  
  889.     """
  890.     def __init__(self, watch_manager, default_proc_fun=ProcessEvent(),
  891.                  read_freq=0, treshold=0, timeout=None):
  892.         """
  893.         Initialization. read_freq, treshold and timeout parameters are used
  894.         when looping.
  895.  
  896.         @param watch_manager: Watch Manager.
  897.         @type watch_manager: WatchManager instance
  898.         @param default_proc_fun: Default processing method.
  899.         @type default_proc_fun: instance of ProcessEvent
  900.         @param read_freq: if read_freq == 0, events are read asap,
  901.                           if read_freq is > 0, this thread sleeps
  902.                           max(0, read_freq - timeout) seconds. But if
  903.                           timeout is None it can be different because
  904.                           poll is blocking waiting for something to read.
  905.         @type read_freq: int
  906.         @param treshold: File descriptor will be read only if its size to
  907.                          read is >= treshold. If != 0, you likely want to
  908.                          use it in combination with read_freq because
  909.                          without that you keep looping without really reading
  910.                          anything and that until the amount to read
  911.                          is >= treshold. At least with read_freq you may sleep.
  912.         @type treshold: int
  913.         @param timeout:
  914.             http://docs.python.org/lib/poll-objects.html#poll-objects
  915.         @type timeout: int
  916.         """
  917.         # watch manager instance
  918.         self._watch_manager = watch_manager
  919.         # file descriptor
  920.         self._fd = self._watch_manager._fd
  921.         # poll object and registration
  922.         self._pollobj = select.poll()
  923.         self._pollobj.register(self._fd, select.POLLIN)
  924.         # This pipe is correctely initialized and used by ThreadedNotifier
  925.         self._pipe = (-1, -1)
  926.         # event queue
  927.         self._eventq = deque()
  928.         # system processing functor, common to all events
  929.         self._sys_proc_fun = _SysProcessEvent(self._watch_manager, self)
  930.         # default processing method
  931.         self._default_proc_fun = default_proc_fun
  932.         # loop parameters
  933.         self._read_freq = read_freq
  934.         self._treshold = treshold
  935.         self._timeout = timeout
  936.  
  937.     def proc_fun(self):
  938.         return self._default_proc_fun
  939.  
  940.     def check_events(self):
  941.         """
  942.         Check for new events available to read, blocks up to timeout
  943.         milliseconds.
  944.  
  945.         @return: New events to read.
  946.         @rtype: bool
  947.         """
  948.         while True:
  949.             try:
  950.                 # blocks up to 'timeout' milliseconds
  951.                 ret = self._pollobj.poll(self._timeout)
  952.             except select.error, err:
  953.                 if err[0] == errno.EINTR:
  954.                     continue # interrupted, retry
  955.                 else:
  956.                     raise
  957.             else:
  958.                 break
  959.  
  960.         if not ret or (self._pipe[0] == ret[0][0]):
  961.             return False
  962.         # only one fd is polled
  963.         return ret[0][1] & select.POLLIN
  964.  
  965.     def read_events(self):
  966.         """
  967.         Read events from device, build _RawEvents, and enqueue them.
  968.         """
  969.         buf_ = array.array('i', [0])
  970.         # get event queue size
  971.         if fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1:
  972.             return
  973.         queue_size = buf_[0]
  974.         if queue_size < self._treshold:
  975.             log.debug('(fd: %d) %d bytes available to read but '
  976.                       'treshold is fixed to %d bytes' % (self._fd,
  977.                                                          queue_size,
  978.                                                          self._treshold))
  979.             return
  980.  
  981.         try:
  982.             # read content from file
  983.             r = os.read(self._fd, queue_size)
  984.         except Exception, msg:
  985.             raise NotifierError(msg)
  986.         log.debug('event queue size: %d' % queue_size)
  987.         rsum = 0  # counter
  988.         while rsum < queue_size:
  989.             s_size = 16
  990.             # retrieve wd, mask, cookie
  991.             s_ = struct.unpack('iIII', r[rsum:rsum+s_size])
  992.             # length of name
  993.             fname_len = s_[3]
  994.             # field 'length' useless
  995.             s_ = s_[:-1]
  996.             # retrieve name
  997.             s_ += struct.unpack('%ds' % fname_len,
  998.                                 r[rsum + s_size:rsum + s_size + fname_len])
  999.             self._eventq.append(_RawEvent(*s_))
  1000.             rsum += s_size + fname_len
  1001.  
  1002.     def process_events(self):
  1003.         """
  1004.         Routine for processing events from queue by calling their
  1005.         associated proccessing function (instance of ProcessEvent).
  1006.         It also do internal processings, to keep the system updated.
  1007.         """
  1008.         while self._eventq:
  1009.             raw_event = self._eventq.popleft()  # pop next event
  1010.             watch_ = self._watch_manager._wmd.get(raw_event.wd)
  1011.             revent = self._sys_proc_fun(raw_event)  # system processings
  1012.             if watch_ and watch_.proc_fun:
  1013.                 watch_.proc_fun(revent)  # user processings
  1014.             else:
  1015.                 self._default_proc_fun(revent)
  1016.         self._sys_proc_fun.cleanup()  # remove olds MOVED_* events records
  1017.  
  1018.  
  1019.     def __daemonize(self, pid_file=None, force_kill=False, stdin=os.devnull,
  1020.                     stdout=os.devnull, stderr=os.devnull):
  1021.         """
  1022.         pid_file: file to which pid will be written.
  1023.         force_kill: if True kill the process associated to pid_file.
  1024.         stdin, stdout, stderr: files associated to common streams.
  1025.         """
  1026.         if pid_file is None:
  1027.             dirname = '/var/run/'
  1028.             basename = sys.argv[0] or 'pyinotify'
  1029.             pid_file = os.path.join(dirname, basename + '.pid')
  1030.  
  1031.         if os.path.exists(pid_file):
  1032.             fo = file(pid_file, 'rb')
  1033.             try:
  1034.                 try:
  1035.                     pid = int(fo.read())
  1036.                 except ValueError:
  1037.                     pid = None
  1038.                 if pid is not None:
  1039.                     try:
  1040.                         os.kill(pid, 0)
  1041.                     except OSError, err:
  1042.                         pass
  1043.                     else:
  1044.                         if not force_kill:
  1045.                             s = 'There is already a pid file %s with pid %d'
  1046.                             raise NotifierError(s % (pid_file, pid))
  1047.                         else:
  1048.                             os.kill(pid, 9)
  1049.             finally:
  1050.                 fo.close()
  1051.  
  1052.  
  1053.         def fork_daemon():
  1054.             # Adapted from Chad J. Schroeder's recipe
  1055.             pid = os.fork()
  1056.             if (pid == 0):
  1057.                 # parent 2
  1058.                 os.setsid()
  1059.                 pid = os.fork()
  1060.                 if (pid == 0):
  1061.                     # child
  1062.                     os.chdir('/')
  1063.                     os.umask(0)
  1064.                 else:
  1065.                     # parent 2
  1066.                     os._exit(0)
  1067.             else:
  1068.                 # parent 1
  1069.                 os._exit(0)
  1070.  
  1071.             fd_inp = os.open(stdin, os.O_RDONLY)
  1072.             os.dup2(fd_inp, 0)
  1073.             fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT)
  1074.             os.dup2(fd_out, 1)
  1075.             fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT)
  1076.             os.dup2(fd_err, 2)
  1077.  
  1078.         # Detach task
  1079.         fork_daemon()
  1080.  
  1081.         # Write pid
  1082.         fo = file(pid_file, 'wb')
  1083.         try:
  1084.             fo.write(str(os.getpid()) + '\n')
  1085.         finally:
  1086.             fo.close()
  1087.  
  1088.         atexit.register(lambda : os.unlink(pid_file))
  1089.  
  1090.  
  1091.     def _sleep(self, ref_time):
  1092.         # Only consider sleeping if read_freq is > 0
  1093.         if self._read_freq > 0:
  1094.             cur_time = time.time()
  1095.             sleep_amount = self._read_freq - (cur_time - ref_time)
  1096.             if sleep_amount > 0:
  1097.                 log.debug('Now sleeping %d seconds' % sleep_amount)
  1098.                 time.sleep(sleep_amount)
  1099.  
  1100.  
  1101.     def loop(self, callback=None, daemonize=False, **args):
  1102.         """
  1103.         Events are read only once time every min(read_freq, timeout)
  1104.         seconds at best and only if the size to read is >= treshold.
  1105.  
  1106.         @param callback: Functor called after each event processing. Expects
  1107.                          to receive notifier object (self) as first parameter.
  1108.         @type callback: callable
  1109.         @param daemonize: This thread is daemonized if set to True.
  1110.         @type daemonize: boolean
  1111.         """
  1112.         if daemonize:
  1113.             self.__daemonize(**args)
  1114.  
  1115.         # Read and process events forever
  1116.         while 1:
  1117.             try:
  1118.                 self.process_events()
  1119.                 if callback is not None:
  1120.                     callback(self)
  1121.                 ref_time = time.time()
  1122.                 # check_events is blocking
  1123.                 if self.check_events():
  1124.                     self._sleep(ref_time)
  1125.                     self.read_events()
  1126.             except KeyboardInterrupt:
  1127.                 # Unless sigint is caught (Control-C)
  1128.                 log.debug('Pyinotify stops monitoring.')
  1129.                 # Stop monitoring
  1130.                 self.stop()
  1131.                 break
  1132.  
  1133.     def stop(self):
  1134.         """
  1135.         Close the inotify's instance (close its file descriptor).
  1136.         It destroys all existing watches, pending events,...
  1137.         """
  1138.         self._pollobj.unregister(self._fd)
  1139.         os.close(self._fd)
  1140.  
  1141.  
  1142. class ThreadedNotifier(threading.Thread, Notifier):
  1143.     """
  1144.     This notifier inherits from threading.Thread for instantiating a separate
  1145.     thread, and also inherits from Notifier, because it is a threaded notifier.
  1146.  
  1147.     Note that everything possible with this class is also possible through
  1148.     Notifier. Moreover Notifier is _better_ under many aspects: not threaded,
  1149.     can be easily daemonized.
  1150.     """
  1151.     def __init__(self, watch_manager, default_proc_fun=ProcessEvent(),
  1152.                  read_freq=0, treshold=0, timeout=None):
  1153.         """
  1154.         Initialization, initialize base classes. read_freq, treshold and
  1155.         timeout parameters are used when looping.
  1156.  
  1157.         @param watch_manager: Watch Manager.
  1158.         @type watch_manager: WatchManager instance
  1159.         @param default_proc_fun: Default processing method.
  1160.         @type default_proc_fun: instance of ProcessEvent
  1161.         @param read_freq: if read_freq == 0, events are read asap,
  1162.                           if read_freq is > 0, this thread sleeps
  1163.                           max(0, read_freq - timeout) seconds.
  1164.         @type read_freq: int
  1165.         @param treshold: File descriptor will be read only if its size to
  1166.                          read is >= treshold. If != 0, you likely want to
  1167.                          use it in combination with read_freq because
  1168.                          without that you keep looping without really reading
  1169.                          anything and that until the amount to read
  1170.                          is >= treshold. At least with read_freq you may sleep.
  1171.         @type treshold: int
  1172.         @param timeout:
  1173.            see http://docs.python.org/lib/poll-objects.html#poll-objects
  1174.            Read the corresponding comment in the source code before changing
  1175.            it.
  1176.         @type timeout: int
  1177.         """
  1178.         # Init threading base class
  1179.         threading.Thread.__init__(self)
  1180.         # Stop condition
  1181.         self._stop_event = threading.Event()
  1182.         # Init Notifier base class
  1183.         Notifier.__init__(self, watch_manager, default_proc_fun, read_freq,
  1184.                           treshold, timeout)
  1185.         # Create a new pipe used for thread termination
  1186.         self._pipe = os.pipe()
  1187.         self._pollobj.register(self._pipe[0], select.POLLIN)
  1188.  
  1189.     def stop(self):
  1190.         """
  1191.         Stop the notifier's loop. Stop notification. Join the thread.
  1192.         """
  1193.         self._stop_event.set()
  1194.         os.write(self._pipe[1], 'stop')
  1195.         threading.Thread.join(self)
  1196.         Notifier.stop(self)
  1197.         self._pollobj.unregister(self._pipe[0])
  1198.         os.close(self._pipe[0])
  1199.         os.close(self._pipe[1])
  1200.  
  1201.     def loop(self):
  1202.         """
  1203.         Thread's main loop. Don't meant to be called by user directly.
  1204.         Call start() instead.
  1205.  
  1206.         Events are read only once time every min(read_freq, timeout)
  1207.         seconds at best and only if the size of events to read is >= treshold.
  1208.         """
  1209.         # When the loop must be terminated .stop() is called, 'stop'
  1210.         # is written to pipe fd so poll() returns and .check_events()
  1211.         # returns False which make evaluate the While's stop condition
  1212.         # ._stop_event.isSet() wich put an end to the thread's execution.
  1213.         while not self._stop_event.isSet():
  1214.             self.process_events()
  1215.             ref_time = time.time()
  1216.             if self.check_events():
  1217.                 self._sleep(ref_time)
  1218.                 self.read_events()
  1219.  
  1220.     def run(self):
  1221.         """
  1222.         Start the thread's loop: read and process events until the method
  1223.         stop() is called.
  1224.         Never call this method directly, instead call the start() method
  1225.         inherited from threading.Thread, which then will call run().
  1226.         """
  1227.         self.loop()
  1228.  
  1229.  
  1230. class Watch:
  1231.     """
  1232.     Represent a watch, i.e. a file or directory being watched.
  1233.  
  1234.     """
  1235.     def __init__(self, **keys):
  1236.         """
  1237.         Initializations.
  1238.  
  1239.         @param wd: Watch descriptor.
  1240.         @type wd: int
  1241.         @param path: Path of the file or directory being watched.
  1242.         @type path: str
  1243.         @param mask: Mask.
  1244.         @type mask: int
  1245.         @param proc_fun: Processing callable object.
  1246.         @type proc_fun:
  1247.         @param auto_add: Automatically add watches on new directories.
  1248.         @type auto_add: bool
  1249.         """
  1250.         for k, v in keys.iteritems():
  1251.             setattr(self, k, v)
  1252.         self.dir = os.path.isdir(self.path)
  1253.  
  1254.     def __repr__(self):
  1255.         """
  1256.         @return: String representation.
  1257.         @rtype: str
  1258.         """
  1259.         s = ' '.join(['%s%s%s' % (Color.FieldName(attr),
  1260.                                   Color.Punctuation('='),
  1261.                                   Color.FieldValue(getattr(self, attr))) \
  1262.                       for attr in self.__dict__ if not attr.startswith('_')])
  1263.  
  1264.         s = '%s%s %s %s' % (Color.Punctuation('<'),
  1265.                             Color.ClassName(self.__class__.__name__),
  1266.                             s,
  1267.                             Color.Punctuation('>'))
  1268.         return s
  1269.  
  1270.  
  1271. class ExcludeFilter:
  1272.     """
  1273.     ExcludeFilter is an exclusion filter.
  1274.     """
  1275.  
  1276.     def __init__(self, arg_lst):
  1277.         """
  1278.         @param arg_lst: is either a list or dict of patterns:
  1279.                         [pattern1, ..., patternn]
  1280.                         {'filename1': (list1, listn), ...} where list1 is
  1281.                         a list of patterns
  1282.         @type arg_lst: list or dict
  1283.         """
  1284.         if isinstance(arg_lst, dict):
  1285.             lst = self._load_patterns(arg_lst)
  1286.         elif isinstance(arg_lst, list):
  1287.             lst = arg_lst
  1288.         else:
  1289.             raise TypeError
  1290.  
  1291.         self._lregex = []
  1292.         for regex in lst:
  1293.             self._lregex.append(re.compile(regex, re.UNICODE))
  1294.  
  1295.     def _load_patterns(self, dct):
  1296.         lst = []
  1297.         for path, varnames in dct.iteritems():
  1298.             loc = {}
  1299.             execfile(path, {}, loc)
  1300.             for varname in varnames:
  1301.                 lst.extend(loc.get(varname, []))
  1302.         return lst
  1303.  
  1304.     def _match(self, regex, path):
  1305.         return regex.match(path) is not None
  1306.  
  1307.     def __call__(self, path):
  1308.         """
  1309.         @param path: path to match against regexps.
  1310.         @type path: str
  1311.         @return: return True is path has been matched and should
  1312.                  be excluded, False otherwise.
  1313.         @rtype: bool
  1314.         """
  1315.         for regex in self._lregex:
  1316.             if self._match(regex, path):
  1317.                 return True
  1318.         return False
  1319.  
  1320.  
  1321. class WatchManagerError(Exception):
  1322.     """
  1323.     WatchManager Exception. Raised on error encountered on watches
  1324.     operations.
  1325.  
  1326.     """
  1327.     def __init__(self, msg, wmd):
  1328.         """
  1329.         @param msg: Exception string's description.
  1330.         @type msg: string
  1331.         @param wmd: Results of previous operations made by the same function
  1332.                     on previous wd or paths. It also contains the item which
  1333.                     raised this exception.
  1334.         @type wmd: dict
  1335.         """
  1336.         self.wmd = wmd
  1337.         Exception.__init__(self, msg)
  1338.  
  1339.  
  1340. class WatchManager:
  1341.     """
  1342.     Provide operations for watching files and directories. Integrated
  1343.     dictionary is used to reference watched items.
  1344.     """
  1345.     def __init__(self, exclude_filter=lambda path: False):
  1346.         """
  1347.         Initialization: init inotify, init watch manager dictionary.
  1348.         Raise OSError if initialization fails.
  1349.  
  1350.         @param exclude_filter: boolean function, returns True if current
  1351.                                path must be excluded from being watched.
  1352.                                Convenient for providing a common exclusion
  1353.                                filter for every call to add_watch.
  1354.         @type exclude_filter: bool
  1355.         """
  1356.         self._exclude_filter = exclude_filter
  1357.         self._wmd = {}  # watch dict key: watch descriptor, value: watch
  1358.         self._fd = LIBC.inotify_init() # inotify's init, file descriptor
  1359.         if self._fd < 0:
  1360.             raise OSError()
  1361.  
  1362.     def __add_watch(self, path, mask, proc_fun, auto_add):
  1363.         """
  1364.         Add a watch on path, build a Watch object and insert it in the
  1365.         watch manager dictionary. Return the wd value.
  1366.         """
  1367.         wd_ = LIBC.inotify_add_watch(self._fd,
  1368.                                      ctypes.create_string_buffer(path),
  1369.                                      mask)
  1370.         if wd_ < 0:
  1371.             return wd_
  1372.         watch_ = Watch(wd=wd_, path=os.path.normpath(path), mask=mask,
  1373.                        proc_fun=proc_fun, auto_add=auto_add)
  1374.         self._wmd[wd_] = watch_
  1375.         log.debug('New %s' % watch_)
  1376.         return wd_
  1377.  
  1378.     def __glob(self, path, do_glob):
  1379.         if do_glob:
  1380.             return iglob(path)
  1381.         else:
  1382.             return [path]
  1383.  
  1384.     def add_watch(self, path, mask, proc_fun=None, rec=False,
  1385.                   auto_add=False, do_glob=False, quiet=True,
  1386.                   exclude_filter=None):
  1387.         """
  1388.         Add watch(s) on given path(s) with the specified mask and
  1389.         optionnally with a processing function and recursive flag.
  1390.  
  1391.         @param path: Path to watch, the path can either be a file or a
  1392.                      directory. Also accepts a sequence (list) of paths.
  1393.         @type path: string or list of string
  1394.         @param mask: Bitmask of events.
  1395.         @type mask: int
  1396.         @param proc_fun: Processing object.
  1397.         @type proc_fun: function or ProcessEvent instance or instance of
  1398.                         one of its subclasses or callable object.
  1399.         @param rec: Recursively add watches from path on all its
  1400.                     subdirectories, set to False by default (doesn't
  1401.                     follows symlinks).
  1402.         @type rec: bool
  1403.         @param auto_add: Automatically add watches on newly created
  1404.                          directories in the watch's path.
  1405.         @type auto_add: bool
  1406.         @param do_glob: Do globbing on pathname.
  1407.         @type do_glob: bool
  1408.         @param quiet: if True raise an WatchManagerError exception on
  1409.                       error. See example not_quiet.py
  1410.         @type quiet: bool
  1411.         @param exclude_filter: boolean function, returns True if current
  1412.                                path must be excluded from being watched.
  1413.                                Has precedence on exclude_filter defined
  1414.                                into __init__.
  1415.         @type exclude_filter: bool
  1416.         @return: dict of paths associated to watch descriptors. A wd value
  1417.                  is positive if the watch has been sucessfully added,
  1418.                  otherwise the value is negative. If the path is invalid
  1419.                  it will be not included into this dict.
  1420.         @rtype: dict of {str: int}
  1421.         """
  1422.         ret_ = {} # return {path: wd, ...}
  1423.  
  1424.         if exclude_filter is None:
  1425.             exclude_filter = self._exclude_filter
  1426.  
  1427.         # normalize args as list elements
  1428.         for npath in self.__format_param(path):
  1429.             # unix pathname pattern expansion
  1430.             for apath in self.__glob(npath, do_glob):
  1431.                 # recursively list subdirs according to rec param
  1432.                 for rpath in self.__walk_rec(apath, rec):
  1433.                     if not exclude_filter(rpath):
  1434.                         wd = ret_[rpath] = self.__add_watch(rpath, mask,
  1435.                                                             proc_fun,
  1436.                                                             auto_add)
  1437.                         if wd < 0:
  1438.                             err = 'add_watch: cannot watch %s (WD=%d)'
  1439.                             err = err % (rpath, wd)
  1440.                             if quiet:
  1441.                                 log.error(err)
  1442.                             else:
  1443.                                 raise WatchManagerError(err, ret_)
  1444.                     else:
  1445.                         # Let's say -2 means 'explicitely excluded
  1446.                         # from watching'.
  1447.                         ret_[rpath] = -2
  1448.     return ret_
  1449.  
  1450.     def __get_sub_rec(self, lpath):
  1451.         """
  1452.         Get every wd from self._wmd if its path is under the path of
  1453.         one (at least) of those in lpath. Doesn't follow symlinks.
  1454.  
  1455.         @param lpath: list of watch descriptor
  1456.         @type lpath: list of int
  1457.         @return: list of watch descriptor
  1458.         @rtype: list of int
  1459.         """
  1460.         for d in lpath:
  1461.             root = self.get_path(d)
  1462.             if root:
  1463.                 # always keep root
  1464.                 yield d
  1465.             else:
  1466.                 # if invalid
  1467.                 continue
  1468.  
  1469.             # nothing else to expect
  1470.             if not os.path.isdir(root):
  1471.                 continue
  1472.  
  1473.             # normalization
  1474.             root = os.path.normpath(root)
  1475.             # recursion
  1476.             lend = len(root)
  1477.             for iwd in self._wmd.items():
  1478.                 cur = iwd[1].path
  1479.                 pref = os.path.commonprefix([root, cur])
  1480.                 if root == os.sep or (len(pref) == lend and \
  1481.                                       len(cur) > lend and \
  1482.                                       cur[lend] == os.sep):
  1483.                     yield iwd[1].wd
  1484.  
  1485.     def update_watch(self, wd, mask=None, proc_fun=None, rec=False,
  1486.                      auto_add=False, quiet=True):
  1487.         """
  1488.         Update existing watch(s). Both the mask and the processing
  1489.         object can be modified.
  1490.  
  1491.         @param wd: Watch Descriptor to update. Also accepts a list of
  1492.                      watch descriptors.
  1493.         @type wd: int or list of int
  1494.         @param mask: Optional new bitmask of events.
  1495.         @type mask: int
  1496.         @param proc_fun: Optional new processing function.
  1497.         @type proc_fun: function or ProcessEvent instance or instance of
  1498.                         one of its subclasses or callable object.
  1499.         @param rec: Recursively update watches on every already watched
  1500.                     subdirectories and subfiles.
  1501.         @type rec: bool
  1502.         @param auto_add: Automatically add watches on newly created
  1503.                          directories in the watch's path.
  1504.         @type auto_add: bool
  1505.         @param quiet: if True raise an WatchManagerError exception on
  1506.                       error. See example not_quiet.py
  1507.         @type quiet: bool
  1508.         @return: dict of watch descriptors associated to booleans values.
  1509.                  True if the corresponding wd has been successfully
  1510.                  updated, False otherwise.
  1511.         @rtype: dict of int: bool
  1512.         """
  1513.         lwd = self.__format_param(wd)
  1514.         if rec:
  1515.             lwd = self.__get_sub_rec(lwd)
  1516.  
  1517.         ret_ = {}  # return {wd: bool, ...}
  1518.         for awd in lwd:
  1519.             apath = self.get_path(awd)
  1520.             if not apath or awd < 0:
  1521.                 err = 'update_watch: invalid WD=%d' % awd
  1522.                 if quiet:
  1523.                     log.error(err)
  1524.                     continue
  1525.                 raise WatchManagerError(err, ret_)
  1526.  
  1527.             if mask:
  1528.                 addw = LIBC.inotify_add_watch
  1529.                 wd_ = addw(self._fd,
  1530.                            ctypes.create_string_buffer(apath),
  1531.                            mask)
  1532.                 if wd_ < 0:
  1533.                     ret_[awd] = False
  1534.                     err = 'update_watch: cannot update WD=%d (%s)' % (wd_,
  1535.                                                                       apath)
  1536.                     if quiet:
  1537.                         log.error(err)
  1538.                         continue
  1539.                     raise WatchManagerError(err, ret_)
  1540.  
  1541.                 assert(awd == wd_)
  1542.  
  1543.             if proc_fun or auto_add:
  1544.                 watch_ = self._wmd[awd]
  1545.  
  1546.             if proc_fun:
  1547.                 watch_.proc_fun = proc_fun
  1548.  
  1549.             if auto_add:
  1550.                 watch_.proc_fun = auto_add
  1551.  
  1552.             ret_[awd] = True
  1553.             log.debug('Updated watch - %s' % self._wmd[awd])
  1554.     return ret_
  1555.  
  1556.     def __format_param(self, param):
  1557.         """
  1558.         @param param: Parameter.
  1559.         @type param: string or int
  1560.         @return: wrap param.
  1561.         @rtype: list of type(param)
  1562.         """
  1563.         if isinstance(param, list):
  1564.             for p_ in param:
  1565.                 yield p_
  1566.         else:
  1567.             yield param
  1568.  
  1569.     def get_wd(self, path):
  1570.         """
  1571.         Returns the watch descriptor associated to path. This method
  1572.         has an prohibitive cost, always prefer to keep the WD.
  1573.         If path is unknown None is returned.
  1574.  
  1575.         @param path: path.
  1576.         @type path: str
  1577.         @return: WD or None.
  1578.         @rtype: int or None
  1579.         """
  1580.         path = os.path.normpath(path)
  1581.         for iwd in self._wmd.iteritems():
  1582.             if iwd[1].path == path:
  1583.                 return iwd[0]
  1584.         log.debug('get_wd: unknown path %s' % path)
  1585.  
  1586.     def get_path(self, wd):
  1587.         """
  1588.         Returns the path associated to WD, if WD is unknown
  1589.         None is returned.
  1590.  
  1591.         @param wd: watch descriptor.
  1592.         @type wd: int
  1593.         @return: path or None.
  1594.         @rtype: string or None
  1595.         """
  1596.         watch_ = self._wmd.get(wd)
  1597.         if watch_:
  1598.             return watch_.path
  1599.         log.debug('get_path: unknown WD %d' % wd)
  1600.  
  1601.     def __walk_rec(self, top, rec):
  1602.         """
  1603.         Yields each subdirectories of top, doesn't follow symlinks.
  1604.         If rec is false, only yield top.
  1605.  
  1606.         @param top: root directory.
  1607.         @type top: string
  1608.         @param rec: recursive flag.
  1609.         @type rec: bool
  1610.         @return: path of one subdirectory.
  1611.         @rtype: string
  1612.         """
  1613.         if not rec or os.path.islink(top) or not os.path.isdir(top):
  1614.             yield top
  1615.         else:
  1616.             for root, dirs, files in os.walk(top):
  1617.                 yield root
  1618.  
  1619.     def rm_watch(self, wd, rec=False, quiet=True):
  1620.         """
  1621.         Removes watch(s).
  1622.  
  1623.         @param wd: Watch Descriptor of the file or directory to unwatch.
  1624.                    Also accepts a list of WDs.
  1625.         @type wd: int or list of int.
  1626.         @param rec: Recursively removes watches on every already watched
  1627.                     subdirectories and subfiles.
  1628.         @type rec: bool
  1629.         @param quiet: if True raise an WatchManagerError exception on
  1630.                       error. See example not_quiet.py
  1631.         @type quiet: bool
  1632.         @return: dict of watch descriptors associated to booleans values.
  1633.                  True if the corresponding wd has been successfully
  1634.                  removed, False otherwise.
  1635.         @rtype: dict of int: bool
  1636.         """
  1637.         lwd = self.__format_param(wd)
  1638.         if rec:
  1639.             lwd = self.__get_sub_rec(lwd)
  1640.  
  1641.         ret_ = {}  # return {wd: bool, ...}
  1642.         for awd in lwd:
  1643.             # remove watch
  1644.             wd_ = LIBC.inotify_rm_watch(self._fd, awd)
  1645.             if wd_ < 0:
  1646.                 ret_[awd] = False
  1647.                 err = 'rm_watch: cannot remove WD=%d' % awd
  1648.                 if quiet:
  1649.                     log.error(err)
  1650.                     continue
  1651.                 raise WatchManagerError(err, ret_)
  1652.  
  1653.             ret_[awd] = True
  1654.             log.debug('watch WD=%d (%s) removed' % (awd, self.get_path(awd)))
  1655.         return ret_
  1656.  
  1657.  
  1658.     def watch_transient_file(self, filename, mask, proc_class):
  1659.         """
  1660.         Watch a transient file, which will be created and deleted frequently
  1661.         over time (e.g. pid file).
  1662.  
  1663.         @attention: Under the call to this function it will be impossible
  1664.         to correctly watch the events triggered into the same
  1665.         base directory than the directory where is located this watched
  1666.         transient file. For instance it would actually be wrong to make these
  1667.         two successive calls: wm.watch_transient_file('/var/run/foo.pid', ...)
  1668.         and wm.add_watch('/var/run/', ...)
  1669.  
  1670.         @param filename: Filename.
  1671.         @type filename: string
  1672.         @param mask: Bitmask of events, should contain IN_CREATE and IN_DELETE.
  1673.         @type mask: int
  1674.         @param proc_class: ProcessEvent (or of one of its subclass), beware of
  1675.                            accepting a ProcessEvent's instance as argument into
  1676.                            __init__, see transient_file.py example for more
  1677.                            details.
  1678.         @type proc_class: ProcessEvent's instance or of one of its subclasses.
  1679.         @return: See add_watch().
  1680.         @rtype: See add_watch().
  1681.         """
  1682.         dirname = os.path.dirname(filename)
  1683.         if dirname == '':
  1684.             return {}  # Maintains coherence with add_watch()
  1685.         basename = os.path.basename(filename)
  1686.         # Assuming we are watching at least for IN_CREATE and IN_DELETE
  1687.         mask |= IN_CREATE | IN_DELETE
  1688.  
  1689.         def cmp_name(event):
  1690.             return basename == event.name
  1691.         return self.add_watch(dirname, mask,
  1692.                               proc_fun=proc_class(ChainIfTrue(func=cmp_name)),
  1693.                               rec=False,
  1694.                               auto_add=False, do_glob=False)
  1695.  
  1696.  
  1697. class Color:
  1698.     normal = "\033[0m"
  1699.     black = "\033[30m"
  1700.     red = "\033[31m"
  1701.     green = "\033[32m"
  1702.     yellow = "\033[33m"
  1703.     blue = "\033[34m"
  1704.     purple = "\033[35m"
  1705.     cyan = "\033[36m"
  1706.     bold = "\033[1m"
  1707.     uline = "\033[4m"
  1708.     blink = "\033[5m"
  1709.     invert = "\033[7m"
  1710.  
  1711.     @staticmethod
  1712.     def Punctuation(s):
  1713.         return Color.normal + s + Color.normal
  1714.  
  1715.     @staticmethod
  1716.     def FieldValue(s):
  1717.         if not isinstance(s, str):
  1718.             s = str(s)
  1719.         return Color.purple + s + Color.normal
  1720.  
  1721.     @staticmethod
  1722.     def FieldName(s):
  1723.         return Color.blue + s + Color.normal
  1724.  
  1725.     @staticmethod
  1726.     def ClassName(s):
  1727.         return Color.red + Color.bold + s + Color.normal
  1728.  
  1729.     @staticmethod
  1730.     def Simple(s, color):
  1731.         if not isinstance(s, str):
  1732.             s = str(s)
  1733.         try:
  1734.             color_attr = getattr(Color, color)
  1735.         except AttributeError:
  1736.             return s
  1737.         return color_attr + s + Color.normal
  1738.  
  1739.  
  1740. def command_line():
  1741.     #
  1742.     # - By default the watched path is '/tmp' for all events.
  1743.     # - The monitoring execution blocks and serve forever, type c^c
  1744.     #   to stop it.
  1745.     #
  1746.     from optparse import OptionParser
  1747.  
  1748.     usage = "usage: %prog [options] [path1] [path2] [pathn]"
  1749.  
  1750.     parser = OptionParser(usage=usage)
  1751.     parser.add_option("-v", "--verbose", action="store_true",
  1752.                       dest="verbose", help="Verbose mode")
  1753.     parser.add_option("-r", "--recursive", action="store_true",
  1754.                       dest="recursive",
  1755.                       help="Add watches recursively on paths")
  1756.     parser.add_option("-a", "--auto_add", action="store_true",
  1757.                       dest="auto_add",
  1758.                       help="Automatically add watches on new directories")
  1759.     parser.add_option("-e", "--events-list", metavar="EVENT[,...]",
  1760.                       dest="events_list",
  1761.                       help=("A comma-separated list of events to watch for - "
  1762.                            "see the documentation for valid options (defaults"
  1763.                            " to everything)"))
  1764.     parser.add_option("-s", "--stats", action="store_true",
  1765.                       dest="stats",
  1766.                       help="Display statistics")
  1767.  
  1768.     (options, args) = parser.parse_args()
  1769.  
  1770.     if options.verbose:
  1771.         log.setLevel(10)
  1772.  
  1773.     if len(args) < 1:
  1774.         path = '/tmp'  # default watched path
  1775.     else:
  1776.         path = args
  1777.  
  1778.     # watch manager instance
  1779.     wm = WatchManager()
  1780.     # notifier instance and init
  1781.     if options.stats:
  1782.         notifier = Notifier(wm, default_proc_fun=Stats(), read_freq=5)
  1783.     else:
  1784.         notifier = Notifier(wm)
  1785.  
  1786.     # What mask to apply
  1787.     mask = 0
  1788.     if options.events_list:
  1789.         events_list = options.events_list.split(',')
  1790.         for ev in events_list:
  1791.             evcode = EventsCodes.ALL_FLAGS.get(ev, 0)
  1792.             if evcode:
  1793.                 mask |= evcode
  1794.             else:
  1795.                 parser.error("The event '%s' specified with option -e"
  1796.                              " is not valid" % ev)
  1797.     else:
  1798.         mask = ALL_EVENTS
  1799.  
  1800.     # stats
  1801.     cb_fun = None
  1802.     if options.stats:
  1803.         def cb(s):
  1804.             print('%s\n%s\n' % (repr(s.proc_fun()),
  1805.                                 s.proc_fun()))
  1806.         cb_fun = cb
  1807.  
  1808.     log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path)
  1809.  
  1810.     wm.add_watch(path, mask, rec=options.recursive, auto_add=options.auto_add)
  1811.     # Loop forever (until sigint signal get caught)
  1812.     notifier.loop(callback=cb_fun)
  1813.  
  1814.  
  1815. if __name__ == '__main__':
  1816.     command_line()
  1817.